//
// Created by liyinbin on 2021/12/25.
//

#ifndef SCYLLA_API_SCYLLA_H_
#define SCYLLA_API_SCYLLA_H_

#include <vector>
#include <string>
#include <string_view>
#include "scylla/api/status.h"
#include "scylla/api/base_store.h"
#include "scylla/api/types.h"

namespace scylla {

    class base_store;

    class scylla_store {
    public:

        /// set the value of the key, if already set, recover it.
        status set(const std::string_view &key, const std::string_view &value);

        /// get the value of the key, if not exist, set = default nil value.
        status get(const std::string_view &key, std::string *value);

        /// set key to hold the string value. if key exist
        status set_xx(const std::string_view &key, const std::string_view &value, const int32_t ttl, int32_t *ret);

        /// atomically set key to value and return the old value.
        /// return error when key exist but has no value.
        status get_and_set(const std::string_view &key, const std::string_view &value, std::string *old_value);

        /// sets or clears the bit at offset in the string value stored at key
        status set_bit(const std::string_view &key, size_t offset, int32_t value, int32_t *ret);

        /// returns the bit value at offset in the string value stored at key
        status get_bit(const std::string_view &key, size_t offset, int32_t *ret);

        /// sets the given keys to their respective values
        /// multi_set replaces existing values with new values
        status multi_set(const std::vector <key_value> &kvs);

        //// Returns the values of all specified keys. For every key
        /// that does not hold a string value or does not exist, the
        /// special value nil is returned
        status multi_get(const std::vector <std::string> &keys,
                         std::vector <value_status> *vss);

        /// Set key to hold string value if key does not exist
        /// return 1 if the key was set
        /// return 0 if the key was not set
        status set_nx(const std::string_view &key, const std::string_view &value, int32_t *ret, const int32_t ttl = 0);

        /// sets the given keys to their respective values.
        /// multi_set_nx will not perform any operation at all even
        /// if just a single key already exists.
        status multi_set_nx(const std::vector <key_value> &kvs, int32_t *ret);

        /// Set key to hold string new_value if key currently hold the give value
        /// return 1 if the key currently hold the give value And override success
        /// return 0 if the key doesn't exist And override fail
        /// return -1 if the key currently does not hold the given value And override fail
        status set_vx(const std::string_view &key, const std::string_view &value, const std::string_view &new_value,
                      int32_t *ret, const int32_t ttl = 0);

        /// delete the key that holds a given value
        /// return 1 if the key currently hold the give value And delete success
        /// return 0 if the key doesn't exist And del fail
        /// return -1 if the key currently does not hold the given value And del fail
        status del_vx(const std::string_view &key, const std::string_view &value, int32_t *ret);

        /// Set key to hold string value if key does not exist
        /// return the length of the string after it was modified by the command
        status set_range(const std::string_view &key, int64_t start_offset,
                         const std::string_view &value, int32_t *ret);

        /// Returns the substring of the string value stored at key,
        /// determined by the offsets start and end (both are inclusive)
        status get_range(const std::string_view &key, int64_t start_offset, int64_t end_offset,
                         std::string *ret);

        /// If key already exists and is a string, this command appends the value at
        /// the end of the string
        /// return the length of the string after the append operation
        status append(const std::string_view &key, const std::string_view &value, int32_t *ret);

        /// Count the number of set bits (population counting) in a string.
        /// return the number of bits set to 1
        /// note: if need to specified offset, set have_range to true
        status bit_count(const std::string_view &key, int64_t start_offset, int64_t end_offset,
                         int32_t *ret, bool have_offset);

        /// Perform a bitwise operation between multiple keys
        /// and store the result in the destination key
        status bit_op(bit_op_type op, const std::string &dest_key,
                      const std::vector <std::string> &src_keys, int64_t *ret);

        /// Return the position of the first bit set to 1 or 0 in a string
        /// bit_pos key 0
        status bit_pos(const std::string_view &key, int32_t bit, int64_t *ret);

        /// bit_pos key 0 [start]
        status bit_pos(const std::string_view &key, int32_t bit,
                       int64_t start_offset, int64_t *ret);

        /// bit_pos key 0 [start] [end]
        status bit_pos(const std::string_view &key, int32_t bit,
                       int64_t start_offset, int64_t end_offset,
                       int64_t *ret);

        /// Decrements the number stored at key by decrement
        /// return the value of key after the decrement
        status decr_by(const std::string_view &key, int64_t value, int64_t *ret);

        /// Increments the number stored at key by increment.
        /// If the key does not exist, it is set to 0 before performing the operation
        status incr_by(const std::string_view &key, int64_t value, int64_t *ret);

        /// Increment the string representing a floating point number
        /// stored at key by the specified increment.
        status incr_by_float(const std::string_view &key, const std::string_view &value, std::string *ret);

        /// Set key to hold the string value and set key to timeout after a given
        /// number of seconds
        status set_ex(const std::string_view &key, const std::string_view &value, int32_t ttl);

        /// Returns the length of the string value stored at key. An error
        /// is returned when key holds a non-string value.
        status strlen(const std::string_view &key, int32_t *len);

        ///// hash
        /// Sets field in the hash stored at key to value. If key does not exist, a new
        /// key holding a hash is created. If field already exists in the hash, it is
        /// overwritten.
        status hash_set(const std::string_view &key, const std::string_view &field, const std::string_view &value,
                        int32_t *res);

        /// Returns the value associated with field in the hash stored at key.
        /// the value associated with field, or nil when field is not present in the
        /// hash or key does not exist.
        status hash_get(const std::string_view &key, const std::string_view &field, std::string *value);

        /// Sets the specified fields to their respective values in the hash stored at
        /// key. This command overwrites any specified fields already existing in the
        /// hash. If key does not exist, a new key holding a hash is created.
        status hash_multi_set(const std::string_view &key,
                              const std::vector <field_value> &fvs);

        /// Returns the values associated with the specified fields in the hash stored
        /// at key.
        /// For every field that does not exist in the hash, a nil value is returned.
        /// Because a non-existing keys are treated as empty hashes, running HMGET
        /// against a non-existing key will return a list of nil values.
        status hash_multi_get(const std::string_view &key,
                              const std::vector <std::string> &fields,
                              std::vector <value_status> *vss);

        /// Returns all fields and values of the hash stored at key. In the returned
        /// value, every field name is followed by its value, so the length of the
        /// reply is twice the size of the hash.
        status hash_get_all(const std::string_view &key,
                            std::vector <field_value> *fvs);

        /// Returns all field names in the hash stored at key.
        status hash_keys(const std::string_view &key,
                         std::vector <std::string> *fields);

        /// Returns all values in the hash stored at key.
        status hash_values(const std::string_view &key,
                           std::vector <std::string> *values);

        /// Sets field in the hash stored at key to value, only if field does not yet
        /// exist. If key does not exist, a new key holding a hash is created. If field
        /// already exists, this operation has no effect.
        status hash_set_nx(const std::string_view &key, const std::string_view &field, const std::string_view &value,
                           int32_t *ret);

        /// Returns the number of fields contained in the hash stored at key.
        /// Return 0 when key does not exist.
        status hash_len(const std::string_view &key, int32_t *ret);

        /// Returns the string length of the value associated with field in the hash
        /// stored at key. If the key or the field do not exist, 0 is returned.
        status hash_strlen(const std::string_view &key, const std::string_view &field, int32_t *len);

        /// Returns if field is an existing field in the hash stored at key.
        /// Return status::Ok() if the hash contains field.
        /// Return status::NotFound() if the hash does not contain field,
        /// or key does not exist.
        status hash_exists(const std::string_view &key, const std::string_view &field);

        /// Increments the number stored at field in the hash stored at key by
        /// increment. If key does not exist, a new key holding a hash is created. If
        /// field does not exist the value is set to 0 before the operation is
        /// performed.
        status hash_incr_by(const std::string_view &key, const std::string_view &field, int64_t value,
                            int64_t *ret);

        /// Increment the specified field of a hash stored at key, and representing a
        /// floating point number, by the specified increment. If the increment value
        /// is negative, the result is to have the hash field value decremented instead
        /// of incremented. If the field does not exist, it is set to 0 before
        /// performing the operation. An error is returned if one of the following
        /// conditions occur:
        ///
        /// The field contains a value of the wrong type (not a string).
        /// The current field content or the specified increment are not parsable as a
        /// double precision floating point number.
        status hash_incr_by_float(const std::string_view &key, const std::string_view &field,
                                  const std::string_view &by, std::string *new_value);

        /// Removes the specified fields from the hash stored at key. Specified fields
        /// that do not exist within this hash are ignored. If key does not exist, it
        /// is treated as an empty hash and this command returns 0.
        status hash_del(const std::string_view &key, const std::vector <std::string> &fields,
                        int32_t *ret);

        /// See SCAN for HSCAN documentation.
        status hash_scan(const std::string_view &key, int64_t cursor, const std::string &pattern,
                         int64_t count, std::vector <field_value> *field_values, int64_t *next_cursor);

        /// Iterate over a Hash table of fields
        /// return next_field that the user need to use as the start_field argument
        /// in the next call
        status hash_scan_x(const std::string_view &key, const std::string start_field, const std::string &pattern,
                           int64_t count, std::vector <field_value> *field_values, std::string *next_field);

    private:

        std::unique_ptr<base_store> _impl;

    };
}  // namespace scylla

#endif  // SCYLLA_API_SCYLLA_H_
